package endpoint

import (
	"center/contact"
	"center/db"
	"common/macaroon"
	"common/pb"
	"common/rs"
	"context"
	"crypto/tls"
	"errors"
	"log"
	"time"

	"github.com/zeebo/errs"
	"go.uber.org/zap"
	"storj.io/drpc/drpcmux"
	"storj.io/drpc/drpcserver"
)

var Error = errs.Class("Endpoint")

type Config struct {
	Address         string        `help:"drpc监听地址" releaseDefault:":10000" default:":10000"`
	Rs              string        `help:"切片配置(required/repair/upload/total)" default:"4/6/8/10"`
	Expiration      time.Duration `help:"文件存储有效期，保存当前时间后会被清理" releaseDefault:"4320h" default:"4320h"`
	MinNodeVersion  string        `help:"最低支持节点版本" default:""`
	AllowExistsNode bool          `help:"获取修复任务上传节点时，是否不排除当前保存节点" default:"false"`
	AllowFoldNum    uint          `help:"此值为允许节点重复的最小数量，为0时则不允许" default:"0"`
	TlsPemFile      string        `help:"tls证书文件"  default:"$ROOT/estore.guanjieai.com.pem"`
	TlsKeyFile      string        `help:"tls证书文件"  default:"$ROOT/estore.guanjieai.com.key"`
}

type MetainfoServer struct {
	log    *zap.Logger
	config *Config
	pb.DRPCMetainfoServer
	// struct fields
	db         *db.CenterDB
	nodeClient contact.NodeClient
	rs         rs.Rs
}

func NewMetainfoServer(log *zap.Logger, db *db.CenterDB, config Config, nodeClient contact.NodeClient) *MetainfoServer {
	rs2, err := rs.FromString(config.Rs)
	if err != nil || !rs2.Valid() {
		panic(Error.New("rs_coder 配置错误：" + config.Rs))
	}
	return &MetainfoServer{
		log:        log,
		db:         db,
		config:     &config,
		nodeClient: nodeClient,
		rs:         rs2,
	}
}

func (s *MetainfoServer) Run(ctx context.Context) error {
	m := drpcmux.New()
	err := pb.DRPCRegisterMetainfo(m, s)
	if err != nil {
		return Error.Wrap(err)
	}
	// create a drpc server
	srv := drpcserver.New(m)
	cert, err := tls.LoadX509KeyPair(s.config.TlsPemFile, s.config.TlsKeyFile)
	if err != nil {
		log.Println(err)
		return err
	}
	tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
	lis, err := tls.Listen("tcp", s.config.Address, tlsConfig)
	if err != nil {
		return err
	}

	// run the server
	// N.B.: if you want TLS, you need to wrap the net.Listener with
	// TLS before passing to Serve here.
	s.log.Info("endpoint start:" + s.config.Address)
	return Error.Wrap(srv.Serve(ctx, lis))
}

func (s *MetainfoServer) validateAuth(ctx context.Context, header *pb.RequestHeader) (_ *macaroon.APIKey, err error) {
	key, err := macaroon.ParseRawAPIKey(header.ApiKey)
	if err != nil {
		s.log.Error("invalid request", zap.Error(err))
		return nil, errors.New("invalid API credentials")
	}
	if key.Head().IsZero() {
		s.log.Error("unauthorized request", zap.Error(err))
		return nil, errors.New("unauthorized request")
	}
	return key, nil
}
